package errors

import (
	"fmt"
	"runtime"
	"strings"
)

type BusinessError struct {
	HTTPStatus int
	Code       int
	Message    string
	Stack      []uintptr // 添加堆栈信息
}

var _ error = (*BusinessError)(nil)

func (e *BusinessError) Error() string {
	return e.Message
}

// ErrorWithStack 返回包含堆栈信息的错误信息
func (e *BusinessError) ErrorWithStack() string {
	if len(e.Stack) == 0 {
		return e.Message
	}

	var sb strings.Builder
	sb.WriteString(e.Message)
	sb.WriteString("\nStack trace:")

	frames := runtime.CallersFrames(e.Stack)
	for {
		frame, more := frames.Next()
		if !more {
			break
		}

		// 过滤掉当前错误包的栈帧，只显示业务代码相关的
		if !strings.Contains(frame.File, "pkg/errors/errors.go") {
			sb.WriteString(fmt.Sprintf("\n\t%s:%d %s",
				frame.File, frame.Line, frame.Function))
		}

		if !more {
			break
		}
	}

	return sb.String()
}

// Format 实现 fmt.Formatter 接口，支持 %+v 格式化输出堆栈
func (e *BusinessError) Format(s fmt.State, verb rune) {
	switch verb {
	case 'v':
		if s.Flag('+') {
			// %+v 格式，输出完整的错误信息和堆栈
			fmt.Fprint(s, e.ErrorWithStack())
			return
		}
		fallthrough
	case 's':
		fmt.Fprint(s, e.Error())
	case 'q':
		fmt.Fprintf(s, "%q", e.Error())
	}
}

// captureStack 捕获当前的堆栈信息
func captureStack() []uintptr {
	const depth = 32
	var pcs [depth]uintptr
	n := runtime.Callers(3, pcs[:]) // 跳过 runtime.Callers, captureStack, 和当前函数
	return pcs[:n]
}

func NewWithFull(httpStatus int, code int, message string) *BusinessError {
	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       code,
		Message:    message,
		Stack:      captureStack(),
	}
}

func New(httpStatus int, message string) *BusinessError {
	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       httpStatus,
		Message:    message,
	}
}

func NewFormatted(httpStatus int, format string, a ...any) *BusinessError {
	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       httpStatus,
		Message:    fmt.Sprintf(format, a...),
	}
}

// NewWithStack 创建带有堆栈信息的错误
func NewWithStack(httpStatus int, code int, message string) *BusinessError {
	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       code,
		Message:    message,
		Stack:      captureStack(),
	}
}

func NewWithStackWithoutCode(httpStatus int, message string) *BusinessError {
	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       httpStatus,
		Message:    message,
		Stack:      captureStack(),
	}
}

// Wrap 包装一个已有的错误，添加新的消息和堆栈信息
func Wrap(err error, httpStatus int, code int, message string) *BusinessError {
	if err == nil {
		return nil
	}

	// 如果原错误已经是 BusinessError，保留原有的堆栈信息
	if be, ok := err.(*BusinessError); ok {
		return &BusinessError{
			HTTPStatus: httpStatus,
			Code:       code,
			Message:    fmt.Sprintf("%s: %s", message, be.Message),
			Stack:      be.Stack, // 保留原有堆栈
		}
	}

	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       code,
		Message:    fmt.Sprintf("%s: %s", message, err.Error()),
		Stack:      captureStack(),
	}
}

// WrapWithStack 包装一个已有的错误，添加新的消息和新的堆栈信息
func WrapWithStack(err error, httpStatus int, code int, message string) *BusinessError {
	if err == nil {
		return nil
	}

	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       code,
		Message:    fmt.Sprintf("%s: %s", message, err.Error()),
		Stack:      captureStack(),
	}
}

func WrapWithStackWithoutCode(err error, httpStatus int, message string) *BusinessError {
	if err == nil {
		return nil
	}

	return &BusinessError{
		HTTPStatus: httpStatus,
		Code:       httpStatus,
		Message:    fmt.Sprintf("%s: %s", message, err.Error()),
		Stack:      captureStack(),
	}
}
